#库的使用
import requests#导入requests库
from bs4 import BeautifulSoup#bs4全称是Beatiful Soup，导入
import csv#导入CSV模块（纯文本文件，排列表格，可以使用excel软件打开）
import matplotlib.pyplot as plt#用于创建可视化的图形图像

#获取大学排名网页内容
def getHTMLText(url):
    # 尝试获取网页内容
    try:
        r = requests.get(url, timeout=30)#获取网页访问状态
        r.raise_for_status()#将编码方式改为中文
        r.encoding = r.apparent_encoding#返回得到的网页内容
        html = r.text#将获取的信息存为text格式
        return html
    #否则获取失败
    except:
        print("爬取失败")
        return None

#提取网页内容中的信息并得到ulist列表
def fillUnivList(ulist, html):
    soup = BeautifulSoup(html, 'html.parser')#利用BeautifulSoup对获取到的网页进行处理
    table = soup.find('table', class_='rk-table')#查找表格
    if table is None:
        print("未找到排名表格")
        return
    tbody = table.find('tbody')#查找标签
    if tbody is None:
        print("未找到<tbody>标签")
        return
    data = tbody.find_all('tr')#查找符合所有的标签，我们找到tbody的孩子标签，也就是tr
    #获取tr下的td标签内容，由于有多个td，所以将得到一个列表
    for tr in data:
        tds = tr.find_all('td')
        if len(tds) < 5:  # 跳过不完整的行
            continue
        td_2 = tds[2].text.strip() if tds[2].text else ""
        td_3 = tds[3].text.strip() if tds[3].text else ""
        # 将得到的td标签中的文字分别添加到ulist列表中
        ulist.append([tds[0].string.strip(), tds[1].find('a').string.strip(),
                      td_2, td_3, tds[4].string.strip()])

#创建一个大学排行.csv的文件
def printUnivList(ulist, num):
    file_name = "大学排行.csv"#表格名
    #读取文件
    with open(file_name, 'w', newline='', encoding='utf-8') as f:
        writer = csv.writer(f)
        # 写入标题：排名，大学名称，省市，类型，总分
        writer.writerow(["排名", "大学名称", "省市", "类型", "总分"])
        # 根据输入的写入量进行写入
        for i in range(num):
            u = ulist[i]
            # 写入每条数据的前五个数据分别是：排名，大学名称，省市，类型，总分
            writer.writerow(u)
            print(f"排名：{u[0]}\t大学名称：{u[1]}\t省市：{u[2]}\t类型：{u[3]}\t总分：{u[4]}")


# 折线图
def drawLineChart(ulist):
    #遍历ulist列表
    ranks = [(u[1]) for u in ulist]
    scores = [float(u[4]) for u in ulist]
    names = [u[0] for u in ulist]
    plt.rcParams['font.sans-serif'] = ['SimHei']#设置字体
    plt.figure(dpi=300)  # 设置图像分辨率为300
    plt.figure(figsize=(30, 30))# 设置画布 大小为30*30
    plt.plot(ranks, scores)
    plt.xlabel('大学名称')#x轴为大学名称
    plt.ylabel('总分')#y轴为总分
    plt.title('大学排行榜折线图')#折线图名为大学排行榜折线图

    for i in range(len(ranks)):
        plt.text(ranks[i], scores[i], names[i], ha='center', va='bottom')

    plt.show()


# 饼图
def generatePieChart(ulist, num):
    provinces = {}
    for i in range(num):
        province = ulist[i][2]
        if province in provinces:
            provinces[province] += 1
        else:
            provinces[province] = 1

    labels = provinces.keys()#饼图每一部分的名称（省市）
    sizes = provinces.values()
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.figure(dpi=300)  # 设置图像分辨率为300
    plt.figure(figsize=(8, 6))# 设置画布大小为8*6
    plt.pie(sizes, labels=labels, autopct='%1.1f%%')#sizes为饼图的占比
    plt.axis('equal') # 显示为圆（避免比例压缩为椭圆）
    plt.title('大学排行前30名的省份分布')#饼图的名称
    plt.show()

    # plt.rcParams['font.sans-serif'] = ['SimHei']


# 柱形图
def printUnivList2(ulist, num):
    ranks = []
    scores = []#y轴
    names = []#x轴

    for i in range(num):
        u = ulist[i]
        ranks.append(int(u[0]))
        scores.append(float(u[4]))
        names.append(u[1])

    plt.rcParams['font.sans-serif'] = ['SimHei']#解决符号乱码问题
    plt.figure(dpi=300)  # 设置图像分辨率为300
    plt.bar(ranks, scores)#绘制条形图,//range()能保持x轴一致
    plt.xlabel('排名')#x轴为排名
    plt.ylabel('总分')#y轴为总分
    plt.title('大学排名')#柱形图的名称

    for i in range(len(ranks)):
        # 在每个柱形图上方显示大学名称
        plt.text(ranks[i], scores[i], names[i], ha='center', va='bottom', fontsize=4)

    plt.show()

#调用以上函数，生成表格与图形
def main():
    ulist = []
    url = 'https://www.shanghairanking.cn/rankings/bcur/202311.html'
    html = getHTMLText(url)
    if html is not None:
        fillUnivList(ulist, html)
        printUnivList(ulist, 30)
        printUnivList2(ulist, 30)  # 柱形图
        drawLineChart(ulist)  # 折线图
        generatePieChart(ulist, 30)  # 饼图

main()


